package cytargetlinker.conversion;


import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import org.bridgedb.IDMapperStack;

import cytargetlinker.conversion.ArgsParser.AFilesAttributes;
import cytargetlinker.conversion.ArgsParser.AFilesIn;
import cytargetlinker.conversion.ArgsParser.AFilesOut;
import cytargetlinker.conversion.ArgsParser.AHelp;
import cytargetlinker.conversion.ArgsParser.GraphBuilder;
import cytargetlinker.conversion.graph.Graph;
import cytargetlinker.conversion.graph.Graph.Edge;

/**
 * Converts predicted miRNA target files from TargetScan to a XGMML network.
 * 
 * The input files for this script can be downloaded here:
 * http://www.targetscan.org
 * 
 * @author martina
 */
public class TargetScan {

	private final static Logger log = Logger.getLogger(TargetScan.class.getName());
	private interface Args extends AHelp, AFilesIn, AFilesOut, AFilesAttributes {}
	private static Args pargs;
	
	/**
	 * MAIN METHOD
	 * USAGE: java -cp conversion.jar cytargetlinker.conversion.TargetScan 
	 * ARGUMENTS: 
	 * -i = input file
	 * -o = output file
	 * --bridgeDbSource = BridgeDb mapping file for the source nodes (gene database)
	 * --bridgeDbTarget = BridgeDb mapping file for the target nodes (miRBase database)
	 * --organism "mouse" or "human" 
	 */
	public static void main(String[] args) throws Exception {
		
		pargs = ArgsParser.parse(args, Args.class);
		TargetScan converter = new TargetScan();
		converter.startConversion();

	}
	
	private IDMapperStack idMapper;
	private Graph graph;
	private String sysCodeSourceIn = "Mb";
	private String sysCodeTargetIn = "L";
	
	private Map<String, Integer> index;
	private Map<String, List<String>> edges;
	private Integer countEdges = 0;
	private List<String> foundConnections;
	private boolean mapping = true;
	
	public TargetScan() throws Exception {
		index = new HashMap<String, Integer>();
		edges = new HashMap<String, List<String>>();
		foundConnections = new ArrayList<String>();	
	}
	
	public void startConversion() throws Exception {
		File in = pargs.getInput().get(0);
		if(in != null) {
			Utils.setUpLogger(log, in, false);
			idMapper = Utils.initIDMapper(pargs.getBridgeDbFileSource(), pargs.getBridgeDbFileTarget());
			if(idMapper == null) {
				mapping = false;
				log.info("no identifier mapping");
			}
			if (pargs.getOrganism() != null) {
				log.info("conversion of TargetScan " + pargs.getOrganism() + " file started ...\n");
					
				ArgsParser.convertAndWrite(pargs, pargs, new GraphBuilder() {
					public Graph buildGraph(File in) throws Exception {
						return importTargetScan(in);
					}
				});
					
				log.info("conversion of TargetScan " + pargs.getOrganism() + " file finalized.\n");
			} else {
				log.severe("Please specify organism. --organism human or mouse.\n");
			}
		} else {
			System.out.println("Please specify input file!");
		}
	}
	
	private List<String> ids;
	
	public Graph importTargetScan(File file) throws IOException {
		ids = new ArrayList<String>();
		graph = new Graph();
		setNetworkAttributes(file);
		
		BufferedReader br = new BufferedReader(new FileReader(file));
		String[] header = br.readLine().split("\t");
		
		index = new HashMap<String, Integer>();
		for(int i = 0; i < header.length; i++) index.put(header[i], i);
		
		String line = null;
		List<String[]> rows = new ArrayList<String[]>();
		while((line = br.readLine()) != null) {
			String [] str = line.split("\t", header.length);
			if(pargs.getOrganism().equals("human") && str[index.get("Gene Tax ID")].equals("9606")) {
				rows.add(line.split("\t", header.length));
			} else if(pargs.getOrganism().equals("mouse") && str[index.get("Gene Tax ID")].equals("10090")) {
				rows.add(line.split("\t", header.length));
			}
		}

		for(String[] row : rows) {
			String geneId = row[index.get("Gene ID")];
			String geneName = row[index.get("Gene Symbol")];
			String mirna = row[index.get("miRNA")];
			String score = row[index.get("context+ score")];

			if (!ids.contains(geneId)) {
				createGeneNode(geneId, geneName);
				ids.add(geneId);
			}
			if (!ids.contains(mirna)) {
				createMiRNANode(mirna);
				ids.add(mirna);
			}
			addEdge(geneId, mirna, score);
		}
		
		log.info(foundConnections.size() + " interactions have been found.\n");
		cleanUp();
		return graph;
	}
	
	private void cleanUp() {
		edges.clear();
		index.clear();
		foundConnections.clear();
	}
	
	private void addEdge(String gene, String mirna, String score) {
		if(edges.containsKey(gene)) {
			if(!edges.get(gene).contains(mirna)) {
				Edge e = graph.addEdge("" + countEdges, graph.getNode(mirna),graph.getNode(gene));
				setEdgeAttributes(e, score);
				
				edges.get(gene).add(mirna);
				foundConnections.add(gene + "\t" + mirna);
				countEdges++;
			}
		} else {
			Edge e = graph.addEdge("" + countEdges, graph.getNode(mirna),graph.getNode(gene));
			setEdgeAttributes(e, score);
			
			List<String> list = new ArrayList<String>();
			list.add(mirna);
			foundConnections.add(gene + "\t" + mirna);
			edges.put(gene, list);
			countEdges++;
		}
	}
	
	private void setEdgeAttributes(Edge e, String score) {
		try {
			Double d = Double.parseDouble(score);
			e.setAttribute("context+ score", d.toString());
		} catch(NumberFormatException ex) {
			e.setAttribute("context+ score", "");
		}
	}
	
	private void createGeneNode(String geneId, String geneName) {
		
		Map<String, String> attr = new HashMap<String, String>();
		attr.put("label", geneName);
		attr.put("name", geneName);
		attr.put("organism", pargs.getOrganism());
		attr.put("EntrezGene", geneId);
		
		if(!mapping) {
			Utils.createNode(graph, geneId, attr);
		} else {
			List<String> sysCodeOut = new ArrayList<String>();
			if(pargs.getOrganism().equals("mouse")) sysCodeOut.add("EnMm");
			if(pargs.getOrganism().equals("human")) sysCodeOut.add("EnHs");
			sysCodeOut.add("H");
			sysCodeOut.add("L");
			
			Utils.createNode(graph, geneId, sysCodeTargetIn, sysCodeOut, attr, idMapper);
		}
	}
	
	private void createMiRNANode(String mirnaName) {
		
		Map<String, String> attr = new HashMap<String, String>();
		attr.put("label", mirnaName);
		attr.put("name", mirnaName);
		attr.put("organism", pargs.getOrganism());
		attr.put("miRNA", mirnaName);
		
		if(!mapping) {
			Utils.createNode(graph, mirnaName, attr);
		} else {
			List<String> sysCodeOut = new ArrayList<String>();
			sysCodeOut.add("Mbm");
			sysCodeOut.add("Mb");
			
			Utils.createNode(graph, mirnaName, sysCodeSourceIn, sysCodeOut, attr, idMapper);
		}
	}
	
	private void setNetworkAttributes(File input) {
		graph.setTitle("TargetScan " + pargs.getOrganism());
		graph.setAttribute(CommonAttributes.DATABASE.getName(), "TargetScan " + pargs.getOrganism());
		graph.setAttribute(CommonAttributes.TYPE.getName(), "predicted miRNA targets");
		graph.setAttribute(CommonAttributes.SOURCE_FILE.getName(), input.getName());
		graph.setAttribute(CommonAttributes.IDENTIFIERS.getName(), "identifiers");
		graph.setAttribute(CommonAttributes.TARGET_DATASOURCE.getName(), "EntrezGene");
		graph.setAttribute(CommonAttributes.SOURCE_DATASOURCE.getName(), "miRBase");
		graph.setAttribute(CommonAttributes.SOURCE_TYPE.getName(), "mirna");
		graph.setAttribute(CommonAttributes.TARGET_TYPE.getName(), "gene");
	}
}
